/*
	Filename	:	bjRuleSetDb.cpp
	Author(s)	:	Ray E. Bornert II
	Date		:	2002-JUL-22
	Copyright	:	(c) 2002 RealTimeGaming

	History		:
*/

//////////////
// INCLUDES //
//////////////
#include "bjrules.h"
#include "bjconn.h"
#include "odbc.h"
#include <string.h>
#include "cardset.h"
#include "bjproto.h"
#include "bjsql.h"

////////////////////
//THE PHASE
#define BJPHASE (2)
////////////////////

/////////////////////////////////////////////
//CODEBASE RULES CACHE
/////////////////////////////////////////////
BJRules	BJRules__m_standardBlackJack(0);
BJRules BJRules__m_pontoon			(1);
BJRules BJRules__m_doubleExposure21	(2);
// bwardlow 29-Aug-2006 #14722 if we don't create these rules then we cannot play the game
//BJRules BJRules__m_caribbean21		(3);
BJRules BJRules__m_super21			(4);
BJRules BJRules__m_spanish21		(5);
BJRules BJRules__m_perfectpairs		(6);
BJRules BJRules__m_european         (7);    // kharmon.  08-13-07.  #19496.  Adding European Blackjack.
BJRules BJRules__m_BlackJack_SD         (8);    // RPF 8.4.4 Adding Single Deck Blackjack.
BJRules BJRules__m_BlackJack_DD         (9);    // RPF 8.4.4 Adding Double Deck Blackjack.
/// [2013-07-09] Charles, Added new rule for 'Zappit Rackcard'.
BJRules BJRules__m_Zappit            (ZAPPIT);		// 11
BJRules BJRules__m_Mini_Blackjack    (MINIBLACKJACK);		// 240	CSN-13874: mini blackjack

//ONE RULE SET FOR EACH MACHINE ID
BJRules* BJRules__m_pRuleSet[ 256 ]=
{
	& BJRules__m_standardBlackJack	,	//0
	& BJRules__m_pontoon			,	//1
	& BJRules__m_doubleExposure21	,	//2
	// bwardlow 29-Aug-2006 #14722 disable Carribean 21
	//& BJRules__m_caribbean21 		,	//3
	NULL                            ,
	& BJRules__m_super21			,	//4
	& BJRules__m_spanish21			,	//5
	& BJRules__m_perfectpairs		,	//6
    & BJRules__m_european           ,   //7   kharmon.  08-13-07.  #19496.  Adding European Blackjack.
	& BJRules__m_BlackJack_SD       ,   //8   RPF 8.4.4  Adding Single Deck Blackjack.
	& BJRules__m_BlackJack_DD,           //9   RPF 8.4.4  Adding Double Deck Blackjack.
	NULL,
	& BJRules__m_Zappit,					// 11	/// [2013-07-09] Charles, Added new rule for 'Zappit Rackcard'
	& BJRules__m_Mini_Blackjack			// 12		CSN-13874: mini blackjack
};
/////////////////////////////////////////////
/////////////////////////////////////////////
/////////////////////////////////////////////


/////////////////////////////////////////////
//DATABASE RULES CACHE
/////////////////////////////////////////////
//ONE RULE SET FOR EACH RULESET ID
BJRules* BJRules::m_pRuleSet[ 256 ];

BJRules::BJRules( BJRules* pRules )
{
	*this = *pRules;
}

BJRules* BJRules::GetRuleSet( uint8 ruleSetId )
{
	return m_pRuleSet[ ruleSetId ];
}

void BJRules::SetRuleSet( uint8 ruleSetId, BJRules* pRules )
{
	if (m_pRuleSet[ ruleSetId ] == NULL) {
		m_pRuleSet[ ruleSetId ] = pRules;
	}
}

void BJRules::NewRuleSet( uint8 ruleSetId, BJRules* pRules )
{
	//make sure there is not an object already there
	BJRules* existingRules = GetRuleSet( ruleSetId );

	if (existingRules)
		return;

	BJRules* newRules = new BJRules( pRules );
	SetRuleSet( ruleSetId, newRules );
}
/////////////////////////////////////////////
/////////////////////////////////////////////
/////////////////////////////////////////////
// FogBugz 1711 - GChapman
// Changed return type of function to match the type being returned :)
bool8 BJConnection::GetMachineOptions( uint8 machid, uint8* pRuleSetId, uint8* pNDecks, uint8 *nBets )
{
	//default is same value as machid
	*pRuleSetId = machid;

	bool8 result = true;

	#if (BJPHASE == 2)
	//query the db for the active ruleSetId for this machine
	result = trackGetMachineOptions( machid, pRuleSetId, pNDecks, nBets );
	#endif

	return result;
}

BJRules* BJConnection::GetRuleSet( uint8 ruleSetId, uint8 nDecks, uint8 nBets )
{
	//default is nada
	BJRules* pRules = NULL;

	//is it already in the cache
	pRules = BJRules::GetRuleSet( ruleSetId );

	//cache the ruleset if necessary
	if (pRules == NULL)
	{
		//default to CODEBASE ruleset
		pRules = BJRules__m_pRuleSet[ ruleSetId ];

        // FogBugz 1711 - GChapman
        // Check pRules before calling the assignment operator.
        // Otherwise we can crash here.
        if ( pRules )
        {
		    //get the rules from the database
		    BJRules rules = *pRules;

		    bool8 result = true;

		    #if (BJPHASE == 2)
		    //query the db for the ruleset
		    result = trackGetRuleSet( ruleSetId, &rules );
		    #endif

			//did we load?
		    if (result)
		    {
			    if (nDecks > 0)
				    rules.SetNDecks( nDecks );

			    if( nBets>0 && nBets<=BJMAXBETS )
				    rules.SetMaxBJBets( nBets );

			    //put it in the cache
			    BJRules::NewRuleSet( ruleSetId, &rules );
		    }

		    //it should be there now
		    pRules = BJRules::GetRuleSet( ruleSetId );
        }
	}
    else
    {   // we want these 2 attribetes from DB !!
		if (nDecks > 0)
			pRules->SetNDecks( nDecks );

		if( nBets>0 && nBets<=BJMAXBETS )
			pRules->SetMaxBJBets( nBets );
    }

    return pRules;
}

BJRules* BJConnection::GetRuleSet()
{
	//default is nada
	BJRules* pRules = NULL;

	//what is the machine id for this connection
	uint8 machid = GetMachId();

	//default ruleset is the machid
	uint8 ruleSetId = machid;

	uint8 nDecks = 0;
	uint8 nBets = 0;

	//find the correct ruleset
	bool8 result = GetMachineOptions( machid, &ruleSetId, &nDecks, &nBets );

    // FogBugz 1711 - Gchapman
    // Check the result of GetMachineOptions() before calling GetRuleSet().
    // If it failed, the data passed to GetRuleSet() would be invalid.
    // The code that checks this function has error handling if GetRuleSet() is
    // NULL (which it will be, because that was the default above, at it will
    // not be assigned below).
    if ( result )
    {
	    //call our sibling
	    pRules = GetRuleSet( ruleSetId, nDecks, nBets );
    }

	//all is well
	return pRules;
}

void BJRules::SetPreclusions( char* actionString, GameAction action )
{
	if (actionString == NULL)
		return;

	int len=strlen(actionString );

	for (int i=0; i<len; i++)
	{
		int actionCode = actionString[i];
		switch( actionCode )
		{
		case ' ': break;
		case 'B': m_preclusion[ Bet			][ action ]=1; break;
		case 'H': m_preclusion[ Hit			][ action ]=1; break;
		case 'S': m_preclusion[ Stand		][ action ]=1; break;
		case 'D': m_preclusion[ Double		][ action ]=1; break;
		case 'P': m_preclusion[ Split		][ action ]=1; break;
		case 'R': m_preclusion[ Surrender	][ action ]=1; break;
		case 'Z': m_preclusion[ Zap		 ][ action ]=1; break;			/// [2013-07-10] Charles. Added for supporting 'Zappit Rackcard'.
	//	case '+': m_preclusion[ Raise		][ action ]=1; break;
		}
	}
}

void BJRules::SetPreclusions( GameAction action, char* actionString )
{
	if (actionString == NULL)
		return;

	int len=strlen(actionString );

	for (int i=0; i<len; i++)
	{
		int actionCode = actionString[i];
		switch( actionCode )
		{
		case ' ': break;
		case 'B': m_preclusion[ action ][ Bet		]=1; break;
		case 'H': m_preclusion[ action ][ Hit		]=1; break;
		case 'S': m_preclusion[ action ][ Stand		]=1; break;
		case 'D': m_preclusion[ action ][ Double	]=1; break;
		case 'P': m_preclusion[ action ][ Split		]=1; break;
		case 'R': m_preclusion[ action ][ Surrender	]=1; break;
		case 'Z': m_preclusion[ action ][ Zap		]=1; break;				/// [2013-07-10] Charles. Added for supporting 'Zappit Rackcard'.
	//	case '+': m_preclusion[ action ][ Raise		]=1; break;
		}
	}
}


void BJRules::SetDeckFilterBit( int cardIndex )
{
	__int64	mask = 1;
	mask <<= cardIndex;
	m_deckFilter |= mask;
}

void BJRules::SetDeckFilter( char* filterString )
{
	//gotta have a valid pointer
	if (filterString == NULL)
		return;

	//gotta have a filter
	if (filterString[0] == 0)
		return;

	//filter has to have an even number of characters
	int len = strlen(filterString);
	if (len & 1)
		return;

	//ok there is pattern code that iterates the filter cardset
	//we will brute force this one
	//each we will atempt to 

	for (int i=0; i<len; i+=2)
	{
		unsigned char srank = filterString[i+0];
		unsigned char ssuit = filterString[i+1];
		for (int rank=0; rank <= 15; rank++ )
		for (int suit=1; suit <=  4; suit++ )

		if (IsCardInSet( rank,suit, srank,ssuit ))
		{
			int index = ((rank-1)*4+suit-1);
			SetDeckFilterBit( index );
		}
	}
}

int BJRules::ValidateImport( BJRules* master )
{
	int iResult;
	int i;

	iResult = memcmp( m_actionRules, master->m_actionRules, sizeof(m_actionRules) );
	if (iResult != 0)
	for (i=0; i<m_nActionRules; i++)
	{
		iResult = memcmp( &m_actionRules[i], &master->m_actionRules[i], sizeof(m_actionRules[0]) );
		if (iResult != 0)
			break;
	}

	iResult = memcmp( m_bonusRules, master->m_bonusRules, sizeof(m_bonusRules) );
	if (iResult != 0)
	for (i=0; i<m_nBonusRules; i++)
	{
		iResult = memcmp( &m_bonusRules[i], &master->m_bonusRules[i], sizeof(m_bonusRules[0]) );
		if (iResult != 0)
			break;
	}


	iResult = memcmp( this, master, sizeof(BJRules) );
	if (iResult != 0)
		return -1;

	return 0;
}

/*
GetRuleSet sql query has successfully executed
we need to move all the query data to this object
*/
int BJRules::ImportFromQuery( ODBCQuery* query, int ruleSetId )
{
	if (query == NULL)
		return (-1);

	//remember the ruleSet Id
	m_ruleSetId = ruleSetId;

	int iResult;

	uint8	byte[8];
	int32	b=0;

	int32	real[8];
	int32	r=0;

	char	stri[8][32];
	int32	s=0;
	int32	size=sizeof(stri[0]);
	memset(stri,0,sizeof(stri));

	uint16	c=1;

	//master record
	if (query->getRow())
	{
		query->getData(c++, &byte[b++]);		//hardAces
		query->getData(c++, &byte[b++]);		//numDecks
		query->getData(c++,  stri[s++], size );	//deckFilter
		query->getData(c++, &byte[b++]);		//maxHands
		query->getData(c++, &byte[b++]);		//playerBust
		query->getData(c++, &byte[b++]);		//dealerBust
		/// [2013-07-16] Charles. Added for supporting 'Zappit Rackcard'.
		query->getData(c++, &byte[b++]);		//dealerPush	

		query->getData(c++, &real[r++]);		//payoutWin
		query->getData(c++, &real[r++]);		//payoutLoss
		query->getData(c++, &real[r++]);		//payoutTie
		query->getData(c++, &real[r++]);		//insureBet
		query->getData(c++, &real[r++]);		//insurePayout
		query->getData(c++, &real[r++]);		//surrenderPayout

		query->getData(c++, stri[s++], size );	//dealScript
		query->getData(c++, stri[s++], size );	//hitAfter
		query->getData(c++, stri[s++], size );	//standAfter
		query->getData(c++, stri[s++], size );	//doubleAfter
		query->getData(c++, stri[s++], size );	//splitAfter
		query->getData(c++, stri[s++], size );	//surrenderAfter
		/// [2013-07-10] Charles. Added for supporting 'Zappit Rackcard'.
		query->getData(c++, stri[s++], size );	//zapAfter

		b=0;
		r=0;
		s=0;

		//int				//byte
		m_isHardAces		= byte[b++];
		m_nDecks			= byte[b++];
		m_maximumHands		= byte[b++];
		m_playerBustTotal	= byte[b++];
		m_dealerBustTotal	= byte[b++];
		/// [2013-07-16] Charles. Added for supporting 'Zappit Rackcard'.
		m_dealerPushTotal	= byte[b++];

		//float				//real
		m_payoutWin			= *((float*)&(real[r++]));
		m_payoutLoss		= *((float*)&(real[r++]));
		m_payoutTie			= *((float*)&(real[r++]));
		m_insureBet			= *((float*)&(real[r++]));
		m_insurePayout		= *((float*)&(real[r++]));
		m_surrenderPayout	= *((float*)&(real[r++]));

					//string
		SetDeckFilter( stri[s++] );
		SetDealScript( stri[s++] );

		SetPreclusions( stri[s++], Hit			);
		SetPreclusions( stri[s++], Stand		);
		SetPreclusions( stri[s++], Double		);
		SetPreclusions( stri[s++], Split		);
		SetPreclusions( stri[s++], Surrender	);
		/// [2013-07-10] Charles. Added for supporting 'Zappit Rackcard'.
		SetPreclusions( stri[s++], Zap	);
	}

	iResult = ImportFromQueryAction( query );
	if (iResult != 0)
		return -2;

	iResult = ImportFromQueryBonus( query );
	if (iResult != 0)
		return -3;

	

//	ValidateImport( BJRules__m_pRuleSet[ m_ruleSetId ] );

	//all is well
	return 0;
}

int BJRules::ImportFromQueryAction(  ODBCQuery* query )
{
	int iResult;

	if (query == NULL)
		return (-1);

	//nActions
	if (!query->getRow())
		return (-2);

	int32 nAction=0;
	query->getData(1, &nAction);

	for (int i=0; i<nAction; i++)
	{
		if (!query->getRow())
			return (-3);

		BJAction action;

		iResult = action.ImportFromQuery( query );
		if (iResult < 0)
			return (-4);

		AddRuleAction( &action );
	}

	return 0;
}

int BJRules::ImportFromQueryBonus(  ODBCQuery* query )
{
	int iResult;

	if (query == NULL)
		return (-1);

	//nBonus
	if (!query->getRow())
		return (-2);

	int32 nBonus=0;
	query->getData(1, &nBonus);

	for (int i=0; i<nBonus; i++)
	{
		if (!query->getRow())
			return (-3);

		BJBonus bonus;

		iResult = bonus.ImportFromQuery( query );
		if (iResult < 0)
			return (-4);

		AddRuleBonus( &bonus );
	}

	return 0;
}

int BJAction::SetAction( char* actionName )
{
	if (strcmp("Bet",actionName) == 0)	m_action = Bet		; else
	if (strcmp("Hit",actionName) == 0)	m_action = Hit		; else
	if (strcmp("Sta",actionName) == 0)	m_action = Stand	; else
	if (strcmp("Dou",actionName) == 0)	m_action = Double	; else
	if (strcmp("Spl",actionName) == 0)	m_action = Split	; else
	if (strcmp("Sur",actionName) == 0)	m_action = Surrender; else
	if (strcmp("Zap",actionName) == 0)	m_action = Zap; else					/// [2013-07-10] Charles. Added for supporting 'Zappit Rackcard'.
	if (strcmp("EOG",actionName) == 0)	m_action = EOG		; else
		return (-1);

	return (0);
}

int BJAction::ImportFromQuery(  ODBCQuery* query )
{
	if (query == NULL)
		return (-1);

	uint8	byte[8];
	int32	b=0;

	char	stri[8][32];
	int32	s=0;
	int32	size=sizeof(stri[0]);
	memset(stri,0,sizeof(stri));

	uint16	c=1;

	query->getData(c++, stri[s++], size );	//who
	query->getData(c++, stri[s++], size );	//actionCode

	query->getData(c++, &byte[b++]);		//minHands
	query->getData(c++, &byte[b++]);		//maxHands
	query->getData(c++, &byte[b++]);		//minCards
	query->getData(c++, &byte[b++]);		//maxCards
	query->getData(c++, &byte[b++]);		//minTotal
	query->getData(c++, &byte[b++]);		//maxTotal
	query->getData(c++, &byte[b++]);		//minSoft
	query->getData(c++, &byte[b++]);		//maxSoft

	query->getData(c++, stri[s++], size );	//patternPFC
	query->getData(c++, stri[s++], size );	//pattern

	b=0;
	s=0;

	m_who		= stri[s++][0];

	if (SetAction( stri[s++] ) < 0)
		return (-2);

	m_minHands	= byte[b++];
	m_maxHands	= byte[b++];
	m_minCards	= byte[b++];
	m_maxCards	= byte[b++];
	m_minTotal	= byte[b++];
	m_maxTotal	= byte[b++];
	m_minSoft	= byte[b++];
	m_maxSoft	= byte[b++];

	strncpy( m_pattern2	, stri[s++], sizeof(m_pattern2) );
	strncpy( m_pattern	, stri[s++], sizeof(m_pattern ) );

	return 0;
}

int BJBonus::ImportFromQuery(  ODBCQuery* query )
{
	if (query == NULL)
		return (-1);

	uint8	byte[8];
	int32	b=0;

	int32	real[8];
	int32	r=0;

	char	stri[8][32];
	int32	s=0;
	int32	size=sizeof(stri[0]);
	memset(stri,0,sizeof(stri));

	uint16	c=1;

	query->getData(c++, stri[s++], size );	//who
	query->getData(c++, stri[s++], size );	//type
	query->getData(c++, stri[s++], size );	//name
	query->getData(c++, stri[s++], size );	//pattern
	query->getData(c++, stri[s++], size );	//patternDUC

	query->getData(c++, &byte[b++]);		//minHands
	query->getData(c++, &byte[b++]);		//maxHands
	query->getData(c++, &byte[b++]);		//minCards
	query->getData(c++, &byte[b++]);		//maxCards
	query->getData(c++, &byte[b++]);		//minTotal
	query->getData(c++, &byte[b++]);		//maxTotal
	query->getData(c++, &byte[b++]);		//minNBets
	query->getData(c++, &byte[b++]);		//maxNBets

	query->getData(c++, &real[r++]);		//bonusTotal
	query->getData(c++, &real[r++]);		//bonusPayout

	b=0;
	r=0;
	s=0;

	//chars
	m_who					= stri[s++][0];
	m_type					= stri[s++][0];

	//strings
	strncpy( m_name			, stri[s++], sizeof(m_name) );
	strncpy( m_pattern		, stri[s++], sizeof(m_pattern) );
	strncpy( m_pattern2		, stri[s++], sizeof(m_pattern2) );
	
	//int			//bytes
	m_minHands		= byte[b++];
	m_maxHands		= byte[b++];
	m_minCards		= byte[b++];
	m_maxCards		= byte[b++];
	m_minTotal		= byte[b++];
	m_maxTotal		= byte[b++];
	m_minNBets		= byte[b++];
	m_maxNBets		= byte[b++];

	//int			//int
	m_bonusTotal	= real[r++];

	//float
	m_bonusPayout	= *((float*)&(real[r++]));

	return 0;
}
